#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <stdint.h>

typedef enum
{
    Com_OK = 0,
    Com_FAIL = -1
} Com_state_t;

typedef struct
{
    char *ptr;    // 数据指针
    int capacity; // 总容量
    int len;      // 已用长度
} sub_buffer_t;

typedef struct
{
    sub_buffer_t sub_buffers[2]; // 双缓冲区
    int read_index;
    int write_index;
    pthread_mutex_t read_lock;
    pthread_mutex_t write_lock;
} dobbuffer_t;

typedef dobbuffer_t *dobbuffer_handle_t;

// ------------------ 初始化 ------------------
/// @brief 创建双缓冲区
/// @param capacity 容量
/// @return dobbuffer_handle_t
dobbuffer_handle_t app_dobbuffer_init(int capacity)
{
    //为dobbuffer_t 结构体申请空间
    dobbuffer_t *dobbuffer = (dobbuffer_t *)malloc(sizeof(dobbuffer_t));
    
    //如果空间未申请到返回NULL
    if (!dobbuffer) return NULL;

    //创建两个缓冲区
    for (int i = 0; i < 2; i++)
    {
        //申请缓冲区
        dobbuffer->sub_buffers[i].ptr = (char *)malloc(capacity);
        //如果缓冲区为空执行👇
        if (!dobbuffer->sub_buffers[i].ptr)
        {
            for (int j = 0; j < i; j++)//如果下标1号缓冲区为空释放0号缓冲区
                free(dobbuffer->sub_buffers[j].ptr);
            free(dobbuffer); //释放dobbuffer结构体
            return NULL; //返回空
        }
        
        dobbuffer->sub_buffers[i].capacity = capacity; //赋值总容量
        dobbuffer->sub_buffers[i].len = 0; //初始化已使用容量
    }

    dobbuffer->read_index = 0;//初始化读索引
    dobbuffer->write_index = 1;//初始化写索引

    pthread_mutex_init(&dobbuffer->read_lock, NULL);//初始化读锁
    pthread_mutex_init(&dobbuffer->write_lock, NULL);//初始化写锁

    return dobbuffer;//返回dobbuffer_handle_t dobbuffer
}

// ------------------ 释放 ------------------
/// @brief 清除双缓冲区
/// @param
/// @return Com_state_t
Com_state_t app_dobbuffer_deinit(dobbuffer_handle_t handle)
{
    if (!handle) return Com_FAIL;//如果传入值为空返回失败

    for (int i = 0; i < 2; i++)
    {
        free(handle->sub_buffers[i].ptr);//释放双缓冲区
        handle->sub_buffers[i].ptr = NULL;
    }

    //销毁读写锁
    pthread_mutex_destroy(&handle->read_lock);
    pthread_mutex_destroy(&handle->write_lock);

    free(handle);//释放 dobbuffer句柄
    return Com_OK;//返回成功
}

// ------------------ 写入 ------------------
/// @brief 写入双缓冲区
/// @param handle
/// @param data
/// @param len
/// @return
Com_state_t app_dobbuffer_write(dobbuffer_handle_t handle, const char *data, uint8_t len)
{
    //handle or data or len 没有值 返回失败
    if (!handle || !data || len == 0) return Com_FAIL;

    dobbuffer_t *dob = handle;//将handle值传递给dob

    pthread_mutex_lock(&dob->write_lock);//上写锁

    //将sub_buffers[write_index]类型给wd
    sub_buffer_t *wb = &dob->sub_buffers[dob->write_index];

    //如果剩余容量不够写 解锁返回失败
    if (wb->capacity - wb->len < len + 1)
    {
        pthread_mutex_unlock(&dob->write_lock);
        return Com_FAIL;
    }

    // 写入长度
    wb->ptr[wb->len] = len;
    wb->len += 1;

    // 写入数据
    memcpy(wb->ptr + wb->len, data, len);
    wb->len += len;

    //解锁
    pthread_mutex_unlock(&dob->write_lock);
    //返回成功
    return Com_OK;
}

// ------------------ 读取 ------------------
/// @brief 读取缓冲区
/// @param dobbuffer
/// @param data
/// @param len
/// @return len读取长度
int app_dobbuffer_read(dobbuffer_handle_t handle, char *data, int max_len)
{
    if (!handle || !data || max_len <= 0 )return 0;

    dobbuffer_t *dob = handle;

    pthread_mutex_lock(&dob->read_lock);//上写锁

    sub_buffer_t *rb = &dob->sub_buffers[dob->read_index];

    // 如果读缓冲区没有数据，尝试交换
    if (rb->len == 0)
    {
        //交换缓冲区时 等待写完并上锁(夺取写权)
        pthread_mutex_lock(&dob->write_lock);
        int tmp = dob->read_index;
        dob->read_index = dob->write_index;
        dob->write_index = tmp;
        rb = &dob->sub_buffers[dob->read_index];
        pthread_mutex_unlock(&dob->write_lock);

        if (rb->len == 0)
        {
            //解除读锁
            pthread_mutex_unlock(&dob->read_lock);
            return 0; // 仍然没有数据 返回0读取失败
        }
    }

    // 读取长度
    uint8_t len = rb->ptr[0];

    //如果目标长度大于最大长度，只读取传递的最大长度 其他舍弃
    if (len > max_len) len = max_len;

    // 复制数据
    memcpy(data, rb->ptr + 1, len);

    // 移动剩余数据 如果剩余长度还够
    if (rb->len > len + 1)
    {
        //目标地址：rb->ptr 复制源：rb->ptr + len + 1 复制长度:rb->len - len - 1
        memmove(rb->ptr, rb->ptr + len + 1, rb->len - len - 1);
        rb->len -= (len + 1);
    }
    else
    {
        rb->len = 0; //不够就已经读完了 长度清零
    }

    pthread_mutex_unlock(&dob->read_lock);

    return len;
}
